#ifndef MainWindow_H
#define MainWindow_H

#include "simodo/shell/document/Document_plugin.h"
#include "simodo/shell/panel/Panel_plugin.h"
#include "simodo/shell/runner/Runner_plugin.h"
#include "DocumentCollector.h"
#include "ShellAccess.h"
#include "LspManagement.h"
#include "RunManagement.h"
#include "GlobalMessages.h"

#include "simodo/lsp-client/SimodoCommandResult.h"

#include <QMainWindow>
#include <QFileSystemModel>
#include <QMap>
#include <QVector>

#include <memory>

class DocumentWindow;
class ShellMdiArea;

QT_BEGIN_NAMESPACE
class QAction;
class QMenu;
class QLabel;
class QMdiSubWindow;
class QPrinter;
class QDockWidget;
class QComboBox;
class QCheckBox;
QT_END_NAMESPACE

namespace shell = simodo::shell;

constexpr int INITIAL_QUANTITY_OF_DYNAMIC_PANELS_BY_PLUGIN = 3;

struct PanelData
{
    shell::PanelAdaptor_interface * adaptor = nullptr;
    QDockWidget *                   dock    = nullptr;
};

struct PanelPluginData
{
    shell::Panel_plugin *   factory = nullptr;
    QString                 plugin_id;
    QString                 plugin_designation;
    std::vector<PanelData>  panels;
    QAction *               menu_action = nullptr;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

    ShellMdiArea *      _mdi_area;
    DocumentCollector   _doc_collector;
    ShellAccess         _shell_access;
    QFileSystemModel    _fs_model;
    LspManagement       _lsp_management;
    RunManagement       _run_management;

    QMap<QString,shell::Document_plugin *>  _document_factories;
    QMap<QString,shell::Panel_plugin *>     _panel_factories;
    QMap<QString,shell::Runner_plugin *>    _runner_factories;
    QVector<shell::Runner_interface *>      _runners;

    QVector<PanelPluginData>  _panels;

    QDockWidget *       _global_messages_dock;
    GlobalMessages *    _global_messages = nullptr;

    QMenu *     _view_menu;
    QMenu *     _window_menu;
    QToolBar *  _main_toolbar;
    QToolBar *  _run_toolbar;

    QLabel *    _modeling_state;
    QLabel *    _modeling_time;
    QLabel *    _document_info;
    QLabel *    _cursor_position_info;
    QLabel *    _statistics_info;

    /// @todo Реализации последних открытых каталогов и документов идентичны -
    /// нужно вынести в отдельный класс
    enum { MaxRecentDirs = 10, MaxRecentFiles = 15 };
    QAction *   _recent_dir_acts[MaxRecentDirs];
    QAction *   _recent_dir_submenu_act;
    QAction *   _set_work_dir_action;
    QAction *   _recent_file_acts[MaxRecentFiles];
    QAction *   _recent_file_submenu_act;

    QAction *   _new_action;
    QAction *   _open_action;
    QAction *   _save_action;
    QAction *   _save_as_action;
    QAction *   _save_all_action;
    QAction *   _print_action;
    QAction *   _print_preview_action;
    QAction *   _print_pdf_action;
    QAction *   _export_action;
    QAction *   _setup_action;

    QAction *   _edit_undo_action;
    QAction *   _edit_redo_action;
#ifndef QT_NO_CLIPBOARD
    QAction *   _copy_action;
    QAction *   _cut_action;
    QAction *   _paste_action;
#endif
    QAction *   _zoom_in_action;
    QAction *   _zoom_out_action;
    QAction *   _zoom_original_action;
    QAction *   _find_action;
    QAction *   _replace_action;

    QAction *   _run_action;
    QAction *   _pause_action;
    QAction *   _stop_action;
    QComboBox * _runners_combo = nullptr;
    QCheckBox * _run_auto_check;
    QVBoxLayout *_run_options_layout = nullptr;

    QAction *   _quick_action;
    QAction *   _modeling_action;
    QAction *   _statusbar_action;
    QAction *   _message_dock_action;
    QAction *   _close_act;
    QAction *   _close_all_act;
    QAction *   _tile_act;
    QAction *   _cascade_act;
    QAction *   _split_horizontally_action;
    QAction *   _split_vertically_action;
    QAction *   _next_act;
    QAction *   _previous_act;
    QAction *   _window_menu_separator_act;
    QAction *   _help_action;

public:
    MainWindow();

    const ShellMdiArea &mdi_area() const { return *_mdi_area; }
    DocumentCollector & document_collector() { return _doc_collector; }
    ShellAccess &       shell() { return _shell_access; }
    QDockWidget *       global_messages_dock() { return _global_messages_dock; }
    GlobalMessages *    global_messages() { return _global_messages; }
    QFileSystemModel &  filesystem_model() { return _fs_model; }
    LspManagement &     lsp() { return _lsp_management; }
    RunManagement &     run_management() { return _run_management; }
    QVector<shell::Runner_interface *> & runners() { return _runners; }
    QComboBox *         runners_combo() { return _runners_combo; }
    QCheckBox *         run_auto_check() { return _run_auto_check; }
    QVBoxLayout *       run_options_layout() { return _run_options_layout; }
    QAction *           run_action() { return _run_action; }
    QAction *           pause_action() { return _pause_action; }
    QAction *           stop_action() { return _stop_action; }

    const QVector<PanelPluginData> & panels() const { return _panels; }

    bool openFile(const QString &file_name, bool create = false);
    bool openReports(const QString &file_name, const std::vector<simodo::lsp::SimodoCommandReport> & reports);
    void sendGlobal(const QString & text, shell::MessageSeverity severity=shell::MessageSeverity::Info);
    void resetPanels();

    DocumentWindow * findDocumentWindow(const QString &file_name) const;

    const QAction * edit_undo_action() const { return _edit_undo_action; }
    const QAction * edit_redo_action() const { return _edit_redo_action; }
#ifndef QT_NO_CLIPBOARD
    const QAction * cut_action() const { return _cut_action; }
    const QAction * copy_action() const { return _copy_action; }
#endif
    void displayModelingState(const QString & info);
    void displayModelingTime(const QString & info);
    void displayDocumentInfo(const QString & info);
    void displayCursorPosition(const QString & info);
    void displayStatistics(const QString & info);

    void updateMenus() { updateMenus_slot(); }

    bool attachNewPanelToPanelPluginData(const PanelPluginData & ppd, bool restore_state = false);

    void executeVisualizationCommand(const QString & command);

public slots:
    void changedCurrentSubWindow(QMdiSubWindow * subwin);

protected:
    virtual void closeEvent(QCloseEvent *event) override;

private slots:
    void updateRecentDirActions();
    void openRecentDir();
    void requestHomeDirectory();
    void setHomeDirectory(const QString & dir);
    void updateRecentFileActions();
    void openRecentFile();
    void newFile();
    void open();
    void save();
    void saveAs();
    void saveAll();
    void print();
    void preview();
    void printPdf();
    void printPreview(QPrinter *);
#ifndef QT_NO_CLIPBOARD
    void cut();
    void copy();
    void paste();
#endif
    void statusBarViewing();
    void help();
    void about();
    void updateMenus_slot();
    void updateViewMenu();
    void updateWindowMenu();
    void regroupDocumentHorizontally(bool checked);
    void regroupDocumentVertically(bool checked);

private:
    shell::Document_plugin * findDocumentPlugin(QString extention);
    DocumentWindow * createDocumentWindow(const QString & path="",
                        std::shared_ptr<shell::DocumentAdaptor_interface> doc_adaptor={},
                        QString extention="");

    QString makeFileFilter(shell::service_t service);
    bool loadFile(const QString &file_name, bool create=false);
    static bool hasRecentDirs();
    void prependToRecentDirs(const QString &dirName);
    void setRecentDirsVisible(bool visible);
    static bool hasRecentFiles();
    void prependToRecentFiles(const QString &file_name);
    void setRecentFilesVisible(bool visible);
    void createActions();
    void createToolbar();
    void createStatusBar();
    void attachPanels();
    void readSettings();
    void writeSettings();
    DocumentWindow * activeDocumentWindow() const;
    QMdiSubWindow * findMdiChild(const QString &file_name) const;

    template <typename INTERFACE>
    void loadPlugins(QMap<QString,INTERFACE *> & container, QString mask);

    friend class ShellAccess;

public:
    static QString combinePanelTitle(shell::Panel_plugin * plugin, const QString & title);

};

#endif
